﻿using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using UnityEngine.AI;

namespace GameToolkit.Editor
{
    [CustomEditor(typeof(NavMeshPrefab), true)]
    public class NavMeshPrefabEditor : UnityEditor.Editor
    {
        SerializedProperty m_NavMeshData;
        SerializedProperty m_IsRuntimeModifier;

        VolumetricBounds volume;

        protected virtual void OnEnable()
        {
            m_NavMeshData = serializedObject.FindProperty("m_NavMeshData");
            m_IsRuntimeModifier = serializedObject.FindProperty("m_IsRuntimeModifier");
            NavAgentTypeDrawer.GetAgentNames();
            volume = ((NavMeshPrefab)target).GetComponent<VolumetricBounds>();

#if !UNITY_2022_3_OR_NEWER
            UnityEditor.AI.NavMeshVisualizationSettings.showNavigation++;
#endif
        }

        protected virtual void OnDisable()
        {
#if !UNITY_2022_3_OR_NEWER
            UnityEditor.AI.NavMeshVisualizationSettings.showNavigation--;
#endif
        }

        public override void OnInspectorGUI()
        {
            EditorGUILayout.BeginHorizontal();
            EditorGUI.BeginDisabledGroup(true);
            EditorGUILayout.PrefixLabel("Build Volume");
            volume = EditorGUILayout.ObjectField(volume, typeof(VolumetricBounds), true) as VolumetricBounds;
            EditorGUI.EndDisabledGroup();
            EditorGUILayout.EndHorizontal();
            if (volume == null)
                EditorGUILayout.HelpBox("NavMeshPrefab Require A VolumetricBounds Component.", MessageType.Warning, true);
            base.OnInspectorGUI();
            EditorGUI.BeginChangeCheck();
            EditorGUI.BeginDisabledGroup(Application.isPlaying);
            EditorGUILayout.PropertyField(m_IsRuntimeModifier);
            EditorGUILayout.PropertyField(m_NavMeshData);
            EditorGUI.EndDisabledGroup();
            if (EditorGUI.EndChangeCheck())
            {
                serializedObject.ApplyModifiedProperties();
            }

            var rect = EditorGUILayout.BeginHorizontal();
            GUILayout.Label(" ");
            EditorGUI.BeginDisabledGroup(Application.isPlaying || volume == null);
            var bake = GUILayout.Button("Bake", "LargeButton", GUILayout.Width(100));
            var clear = GUILayout.Button("Clear", "LargeButton", GUILayout.Width(100));
            EditorGUI.EndDisabledGroup();
            EditorGUILayout.EndHorizontal();

            if (bake)
                BakeNavMeshData();
            if (clear)
                ClearNavMeshData();
        }

        void ClearNavMeshData()
        {
            var data = m_NavMeshData.objectReferenceValue as NavMeshData;
            if (data == null)
                return;
            m_NavMeshData.objectReferenceValue = null;
            serializedObject.ApplyModifiedProperties();
            var path = AssetDatabase.GetAssetPath(data);
            if (string.IsNullOrEmpty(path))
                Object.DestroyImmediate(data, true);
            else
                AssetDatabase.DeleteAsset(path);
            ((NavMeshPrefab)target).UpdateNavMeshData();
        }

        bool UseNavSrc(INavMeshSource src, NavMeshPrefab root)
        {
            if (volume == null || src.gameObject == root.gameObject)
                return false;
            if (((1 << src.gameObject.layer) & root.Layers) == 0)
                return false;
            if (!src.AffectOnAgent(root.AgentTypeID))
                return false;
            var bounds = VolumetricBounds.CalcuateBounds(root.transform.worldToLocalMatrix * src.gameObject.transform.localToWorldMatrix, src.localBounds);
            if (!bounds.Intersects(volume.localBounds))
                return false;
            var p = src is NavMeshPrefab ? src.transform.parent : src.transform;
            var pref = p == null ? null : p.GetComponentInParent<NavMeshPrefab>();
            if (pref != null && pref != root)
                return false;
            return true;
        }

        void GetBuildSource(NavMeshPrefab builder, GameObject root, ICollection<INavMeshSource> outputs)
        {
            if ((builder.CollectFrom & NavMeshPrefab.ECollect.InChildren) != 0)
            {
                var children = root.GetComponentsInChildren<INavMeshSource>();
                foreach (var src in children)
                {
                    if (UseNavSrc(src, builder))
                        outputs.Add(src);
                }
            }
            if ((builder.CollectFrom & NavMeshPrefab.ECollect.InParent) != 0)
            {
                var parents = root.GetComponentsInParent<INavMeshSource>();
                foreach (var src in parents)
                {
                    if (UseNavSrc(src, builder))
                        outputs.Add(src);
                }
            }
        }

        void BakeNavMeshData()
        {
            var root = (NavMeshPrefab)target;
            
            var cancel = EditorUtility.DisplayCancelableProgressBar("Baking NavMeshData", "Collecting NavMeshSources...", 0);
            if (cancel)
                return;
            var sources = new List<NavMeshBuildSource>();
            HashSet<INavMeshSource> navsrcs = new HashSet<INavMeshSource>();
            //var builders = RuntimeNavMeshBuilder.GetBuildersEnumerator();
            //while (builders.MoveNext())
            //{
            //    var builder = builders.Current;
            //    if (builder.AffectOnAgent(root.AgentTypeID) && ((1 << builder.gameObject.layer) & root.Layers) != 0)
            //        navsrcs.Add(builder);
            //}
            GetBuildSource(root, root.gameObject, navsrcs);
            if (root.AdditionalCollectionRoot != null)
                GetBuildSource(root, root.AdditionalCollectionRoot, navsrcs);
            foreach (var src in navsrcs)
            {
                src.GetBuildSource(root.AreaType, sources);
            }

            var data = m_NavMeshData.objectReferenceValue as NavMeshData;
            if (data == null)
            {
                data = new NavMeshData();
                data.name = root.name + "-NavMesh-" + NavMesh.GetSettingsNameFromID(root.AgentTypeID);
            }
            data.position = root.transform.position;
            data.rotation = root.transform.rotation;
            Bounds bounds = new Bounds(volume.m_BoundsCenter, volume.m_BoundsSize);
            var update = UnityEngine.AI.NavMeshBuilder.UpdateNavMeshData(data, NavMesh.GetSettingsByID(root.AgentTypeID), sources, bounds);
            if (update)
            {
                m_NavMeshData.objectReferenceValue = data;
                serializedObject.ApplyModifiedProperties();
                EditorUtility.SetDirty(data);
            }
            else
            {
                EditorUtility.DisplayDialog("Error", "Failed To Bake NavMeshData!!!", "OK");
                return;
            }
            EditorUtility.ClearProgressBar();
            EditorUtility.SetDirty(data);
            if (!AssetDatabase.Contains(data))
            {
                var path = EditorUtility.SaveFilePanelInProject("Save NavMeshData", data.name, "asset", "Save NavMeshData");
                if (!string.IsNullOrEmpty(path))
                {
                    if (File.Exists(path))
                        AssetDatabase.DeleteAsset(path);
                    data.name = Path.GetFileNameWithoutExtension(path);
                    AssetDatabase.CreateAsset(data, path);
                    AssetDatabase.ImportAsset(path);
                }
            }
            root.UpdateNavMeshData();
        }

    }
}